/*******************************************************************************
 * Copyright (c) 2001, 2002 Boris Pruessmann and others.
 * All rights reserved.   This program and the accompanying materials
 * are made available under the terms of the Common Public License v0.5
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/cpl-v05.html
 * 
 * Contributors:
 *    Boris Pruessmann - Initial implementation.
 ******************************************************************************/
package net.sourceforge.perforce.ui.views;

import net.sourceforge.perforce.core.PerforceCorePlugin;
import net.sourceforge.perforce.core.api.IPerforceListener;
import net.sourceforge.perforce.core.api.IPerforceServer;
import net.sourceforge.perforce.core.resources.RemoteFile;
import net.sourceforge.perforce.ui.IPerforceUIConstants;
import net.sourceforge.perforce.ui.PerforceUIPlugin;
import net.sourceforge.perforce.ui.action.OpenRemoteFileAction;
import net.sourceforge.perforce.ui.internal.Policy;
import net.sourceforge.perforce.ui.internal.filters.DeletedFilesFilter;
import net.sourceforge.perforce.ui.model.AllRootsElement;
import net.sourceforge.perforce.ui.model.RemoteContentProvider;
import net.sourceforge.perforce.ui.wizards.NewLocationWizard;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerSorter;
import org.eclipse.jface.wizard.WizardDialog;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.IViewSite;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.dialogs.PropertyDialogAction;
import org.eclipse.ui.model.WorkbenchLabelProvider;
import org.eclipse.ui.part.DrillDownAdapter;
import org.eclipse.ui.part.ViewPart;

/**
  * @version $Revision: 189 $
  * @author	 <a href="mailto:bpruessmann@users.sourceforge.net">Boris Pruessmann</a>
  */
public class ServerView extends ViewPart implements IPerforceListener
{
	// ---------------------------------------------------------------------------- Public Constants
	
	/** Unique identifier of the Server view. */
	public static final String ID = PerforceUIPlugin.ID + ".views.ServerView";         //$NON-NLS-1$
	
	// --------------------------------------------------------------------------- Private Constants
	
	/** Used for storing state in the memento. */
	private static final String TAG_SHOW_DELETED_FILES = "showDeletedFiles";           //$NON-NLS-1$
	
	// --------------------------------------------------------------------------- Private Variables

	/** The Root element. */
	private AllRootsElement root;	
	/** Stores the tree viewer used to display the perforce servers. */
	private TreeViewer viewer;
	/** Sotres the drill down adapter. */
	private DrillDownAdapter drillPart;
	/** Stores the action for "New->Perforce Server" */
	private Action newAction;
	/** Stores the action for "Refresh". */
	private Action refreshAction;
	/** Stores the action for "Properties". */
	private Action propertiesAction;
	/** Stores the action for "Show Delete Depot Files" */
	private Action showDeletedFilesAction;
	/** Stores the action used on double clicking. */
	private OpenRemoteFileAction openRemoteFileAction;
	/** Stores the memento. */
	private IMemento memento;
	/** Stores the deleted files filter. */
	private DeletedFilesFilter deletedFilesFilter;

	// ------------------------------------------------------------------------------ Public Methods

	/** @see ViewPart#init(IViewSite, IMemento) */
	public void init(IViewSite site, IMemento memento)
		throws PartInitException
	{
		super.init(site, memento);
		this.memento = memento;
	}

	/** @see IWorkbenchPart#createPartControl(Composite) */
	public void createPartControl(Composite parent)
	{
		root = new AllRootsElement();
		
		viewer = new TreeViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
		viewer.setContentProvider(new RemoteContentProvider());	
		viewer.setLabelProvider(new WorkbenchLabelProvider());
		viewer.setSorter(new InternalSorter());
		viewer.getControl().addKeyListener(new KeyAdapter() 
		{
			public void keyPressed(KeyEvent event) 
			{
				if (event.keyCode == SWT.F5) 
				{
					getRefreshAction().run();
				}
				else
				if (event.keyCode == SWT.INSERT)
				{
					getNewAction().run();
				}				
			}
		});

		getSite().setSelectionProvider(viewer);
		viewer.setInput(root);		
		
		contributeActions();
		memento = null;
		
		PerforceCorePlugin.getServerManager().addPerforceListener(this);
	}
	
	/** @see org.eclipse.ui.IWorkbenchPart#dispose() */
	public void dispose() 
	{
		PerforceCorePlugin.getServerManager().removePerforceListener(this);
		super.dispose();
	}

	/** @see IWorkbenchPart#setFocus() */
	public void setFocus()
	{
		viewer.getControl().setFocus();
	}

	/** @see ViewPart#saveState(IMemento) */
	public void saveState(IMemento memento)
	{
		if (deletedFilesFilter == null)
		{
			memento.putString(TAG_SHOW_DELETED_FILES, "true");
		}
		else
		{
			memento.putString(TAG_SHOW_DELETED_FILES, "false");
		}
	}

	// ---------------------------------------------------------- Public Methods (IPerforceListener)

	/** @see IPerforceListener#perforceServerAdded(IPerforceServer) */
	public void perforceServerAdded(IPerforceServer server) 
	{
		Display display = getSite().getShell().getDisplay();
		display.asyncExec(
			new Runnable()
			{
				public void run() 
				{
					viewer.refresh();					
				}
			});		
	}
	
	/** @see IPerforceListener#perforceServerRemoved(IPerforceServer) */
	public void perforceServerRemoved(IPerforceServer server) 
	{
		Display display = getSite().getShell().getDisplay();
		display.asyncExec(
			new Runnable()
			{
				public void run() 
				{
					viewer.refresh();					
				}
			});
	}

	// ------------------------------------------------------------------------------------- Methods
	
	/** Returns the TreeViewer.
	  * 
	  * @return the viewer.
	  */
	TreeViewer getViewer()
	{
		return viewer;
	}

	/** Call this method to retrieve the drill part.
	  * 
	  * @return the drill part.
	  */
	DrillDownAdapter getDrillPart()
	{
		return drillPart;
	}

	/** Call this method to retrieve the new action.
	  *  
	  * @return the action.
	  */
	Action getNewAction()
	{
		return newAction;
	}
	
	/** Call this method to retrieve the refresh action. 
	  * 
	  * @return the refresh action.
	  */
	Action getRefreshAction()
	{
		return refreshAction;
	}
	
	/** Handles double clicks. 
	  * 
	  * @param e the DoubleClick event.
	  */
	void handleDoubleClick(DoubleClickEvent e)
	{
		ISelection selection = e.getSelection();
		if (selection instanceof IStructuredSelection) 
		{
			IStructuredSelection structured = (IStructuredSelection) selection;
			if (structured.size() == 1) 
			{
				Object first = structured.getFirstElement();
				if (first instanceof RemoteFile) 
				{
					// It's a file, open it.
					openRemoteFileAction.selectionChanged(null, selection);
					openRemoteFileAction.run(null);
				} 
				else 
				{
					// Try to expand/contract
					viewer.setExpandedState(first, !viewer.getExpandedState(first));
				}
			}
		}
	}

	// ----------------------------------------------------------------------------- Private Methods

	/** Creates and registers actions with menu and toolbar. */
	private void contributeActions()
	{
		createRefreshAction();
		createNewAction();
		createPropertiesAction();
		createShowDeletedFilesAction();
		createOpenRemoteFileAction();

		drillPart = new DrillDownAdapter(viewer);
		contributeMenuActions();		
		contributeToolbarActions();
		contributeDoubleClickListener();
	}

	/** Called to create the action used to open remote files. */
	private void createOpenRemoteFileAction() 
	{
		openRemoteFileAction = new OpenRemoteFileAction();
	}

	/** This method add double click listeners to the view. */
	private void contributeDoubleClickListener() 
	{
		viewer.addDoubleClickListener(new IDoubleClickListener() 
		{
			public void doubleClick(DoubleClickEvent e) 
			{
				handleDoubleClick(e);
			}
		});
	}

	/** This method contributes the created actions to the toolbar. */
	private void contributeToolbarActions() 
	{
		// Create the local tool bar
		IActionBars bars = getViewSite().getActionBars();
		IToolBarManager toolBarManager = bars.getToolBarManager();
		drillPart.addNavigationActions(toolBarManager);
		toolBarManager.add(refreshAction);
		toolBarManager.update(false);	
		
		bars.getMenuManager().add(showDeletedFilesAction);		
	}

	/** This method contributes the created action to the context menu. */
	private void contributeMenuActions() 
	{
		Tree tree = viewer.getTree();
		
		MenuManager menuManager = new MenuManager();
		Menu menu = menuManager.createContextMenu(tree);
		menuManager.addMenuListener(new IMenuListener()
		{
			/** @see IMenuListener#menuAboutToShow(IMenuManager) */
			public void menuAboutToShow(IMenuManager manager)
			{
				MenuManager newMenuManager =
					new MenuManager(Policy.bind("ServerView.newSubMenu"),              //$NON-NLS-1$
						IWorkbenchActionConstants.GROUP_ADD);
				newMenuManager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
				newMenuManager.add(getNewAction());
				manager.add(newMenuManager);
				
				MenuManager goToMenuManager =
					new MenuManager(Policy.bind("ServerView.goToSubMenu"),             //$NON-NLS-1$
					IWorkbenchActionConstants.GROUP_ADD);
				getDrillPart().addNavigationActions(goToMenuManager);
				manager.add(goToMenuManager);
				
				manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
				manager.add(new Separator());
				manager.add(getRefreshAction());
				manager.add(propertiesAction);				
			}
		});
		menuManager.setRemoveAllWhenShown(true);
		tree.setMenu(menu);
		getSite().registerContextMenu(menuManager, viewer);	
	}

	/** Called to create the "New->Perforce Server"  action. */
	private void createNewAction() 
	{
		ImageDescriptor descriptor = 
			PerforceUIPlugin.getPlugin().getImageDescriptor(IPerforceUIConstants.IMG_NEWLOCATION);
			
		newAction = new Action(Policy.bind("ServerView.new"), descriptor)              //$NON-NLS-1$
		{
			public void run() 
			{
				NewLocationWizard wizard = new NewLocationWizard();
				WizardDialog dialog = new WizardDialog(getViewer().getTree().getShell(), wizard);
				dialog.open();
			}
		};
	}

	/** Called to create the "Properties" action. */
	private void createPropertiesAction() 
	{
		propertiesAction = new PropertyDialogAction(viewer.getTree().getShell(), viewer);		
	}


	/** Called to create the "Refresh" action. */
	private void createRefreshAction() 
	{
		ImageDescriptor descriptor = 
			PerforceUIPlugin.getPlugin().getImageDescriptor(IPerforceUIConstants.IMG_REFRESH);

		refreshAction = new Action(Policy.bind("ServerView.refresh"), descriptor)      //$NON-NLS-1$
			{
				public void run()
				{
					getViewer().refresh();
				}
			};
	}

	/** Called to create the "Show Deleted Files" action. */
	private void createShowDeletedFilesAction()
	{
		boolean state = false;
		if (memento != null)
		{
			String stateString = memento.getString(TAG_SHOW_DELETED_FILES);
			state =	Boolean.valueOf(stateString).booleanValue();
			if (!state)
			{
				deletedFilesFilter = new DeletedFilesFilter();
				viewer.addFilter(deletedFilesFilter);
			}
		}
		
		showDeletedFilesAction = 
			new Action(Policy.bind("ServerView.showDeletedFiles.label"))               //$NON-NLS-1$
			{
				public void run()
				{
					if (isChecked())
					{
						getViewer().removeFilter(deletedFilesFilter);
						deletedFilesFilter = null;
					}
					else
					{
						deletedFilesFilter = new DeletedFilesFilter();
						getViewer().addFilter(deletedFilesFilter);
					}
				}
			};
			
		showDeletedFilesAction.setToolTipText(
			Policy.bind("ServerView.showDeletedFiles.tooltip"));                       //$NON-NLS-1$

		showDeletedFilesAction.setChecked(state);
	}
	
	// ---------------------------------------------------------------------- Private Nested Classes
	
	/**
	  * Class responsible for sorting the tree.
	  */
	private static class InternalSorter extends ViewerSorter
	{
		/** @see ViewerSorter#compare(Viewer, Object, Object) */
		public int compare(Viewer viewer, Object e1, Object e2)
		{
			if (e1 instanceof IPerforceServer && e2 instanceof IPerforceServer)
			{
				IPerforceServer server1 = (IPerforceServer) e1;
				IPerforceServer server2 = (IPerforceServer) e2;
				
				return server1.getUniqueName().compareTo(server2.getUniqueName());
			}
			else
			{
				return super.compare(viewer, e1, e2);
			}
		}
	}
}
